#include <map>
#include <cstdlib>
#include <string>
#include <stdexcept>

#include "Scene.hpp"


using namespace std;

typedef map<std::string, Scene::TypeDescriptor> ObjectDescriptionTable;

static ObjectDescriptionTable *dtable = NULL;
static struct SceneFini {
~SceneFini() {
    delete dtable;
}
} sceneFini;

void Scene::RegisterType(const Scene::TypeDescriptor &descriptor) {
    if (dtable == NULL) {
        dtable = new ObjectDescriptionTable;
    }

    if (dtable->find(descriptor.name) != dtable->end()) {
        throw logic_error("Object dessciptor " + descriptor.name + " already registered");
    }
    (*dtable)[descriptor.name] = descriptor;
}

class SceneImpl : public Scene {
    const TypeDescriptor* FindTypeDescriptor(const std::type_info &t) {
        ObjectDescriptionTable::const_iterator i = dtable->find(t.name());
        if (i == dtable->end()) {
            return NULL;
        }
        return &i->second;
    }

    virtual void* AllocateBase(const std::type_info &t) {
        int size;
        const TypeDescriptor *d = FindTypeDescriptor(t);

        if (d == NULL) {
            return NULL;
        }

        size = d->size;
        void *object = malloc(size);

        // Create Object
        d->constructor(object);

        // Create Shadow Objects

        return object;
    }

    virtual void* DestroyBase(const std::type_info &t, void *object) {
        const TypeDescriptor *d = FindTypeDescriptor(t);

        if (d == NULL) {
            return object;
        }

        // Destroy Shadow Objects

        // Destroy Object
        d->destructor(object);

        // Release memory
        free(object);
        return NULL;
    }
};

Scene* Scene::Factory() {
    return new SceneImpl;
}
